﻿/**
 * 选择模块 - 核心模块 -- 可选
 */
(function(E, $){

var Consts = E.consts, Event = E.mod('Event');

E.mod("Selection", {
	get: function( what, e ){
		what = what || 'text';
		if( 'text' == what ){
			return Selection.get();
			
		}else if( 'html' == what ){
			return Selection.get('html');
			
		}else if( 'cursorElement' == what || 'element' == what ){
			return Event.getTarget();
		}else {
			return Selection.getSelection();
		}
	},
	
	getText: function( ){
		return Selection.get( );
	},
	
	getHtml: function( ){
		return Selection.get('html');
	},
	
	getElement: function(e){
		return Event.getTarget();
	},
	
	set: function( html ){
		Selection.set( html );
	},
	
	saveRange: function(){
		return Selection.saveRange();
	},
	
	getRange: function(){
		return Selection.getRange();
	},
	
	getElementByCursor: function(){
		return Selection.getElementByCursor();
	},
	
	selectedText: function(text) {
		return Selection.selectedText(text);
	},
	
	cursorToNext: function(el) {
		return Selection.cursorToNext(el);
	},
	
	selectNode: function(node, dir) {
		Selection.init();
		return Selection.selectNode(node, dir);
	},
	
	getBookmark: function( ) {
		Selection.init();
		return Selection.getBookmark( );
	},
	clearBookmark: function( ){
		return Selection.clearBookmark( );
	},
	moveToBookmark: function( storedSelection ) {
		Selection.init();
		return Selection.moveToBookmark( storedSelection );
	}
});

var Selection = {
	dom: null,
	win: null,
	ranges: {},
	
	init: function(){
		this.dom = E.get('document');
		this.win = E.get('window');
	},
	
	get: function( mode ){
		mode = mode || 0;
		this.init();
		var html = '', dom = this.dom, win = this.win;
		
		if( !dom || !win ){
			E.log('run', '');
			return '';
		}
		
		if(dom.selection){// only ie
			html = (mode) ? dom.selection.createRange().htmlText 
				: dom.selection.createRange().text;
		}else if(win.getSelection){
		//chrome opera firefox
			//win.getSelection() 返回值是 object 对象
			html = win.getSelection(); // only text
		}else if(dom.getSelection) {
			html = dom.getSelection(); // only text
		} 
		
		return html ? html : '';
	},
	
	getHtml: function(){
		return this.get(1);
	},
	
	getSelection: function( ){
		var win = self.win, dom = self.dom;
		return (Consts.IE) ? dom.selection: win.getSelection();
	},
	
	set: function( html ){
		if (html === ''){return;}
		var self = this;
		self.init();
		E.focus();
		
		var win = self.win, dom = self.dom, range = self.ranges[E.eid];
		if (Consts.IE) {
			if( !range ){
				E.$('body').prepend( html );
				E.focus();
				return;
			}
			if( range.select ){ range.select(); }
			// 在ie9下，段首位置插入div时会导致一个空的p在插入的div之前
			// 但是没有找到好的办法去避免这个问题
			// 2012-11-27 潘雪鹏
			
			// 还不清楚什么情况下会遇到这个分支，先注释了
			// 2011-05-08 潘雪鹏
			/*
			if ( range.item ) {
				try {
					range.item(0).outerHTML = html;
				} catch(e) {
					var el = range.item(0), parent = el.parentNode;
					parent.removeChild(el);
					if (parent.nodeName.toLowerCase() != 'body'){
						parent = parent.parentNode;
					}
					parent.innerHTML(html + parent.innerHTML);
				}
			}
			*/
			// 在选择范围当中包含table 时可能导致运行时错误
			try{
				if( range.pasteHTML ){
					range.pasteHTML(html);
				}else{
					// 异常
					E.$('body').append( html );
				}
			}catch(e){
				// log
			}
		} else {
		// 其他浏览器
			if( E.Plugin && E.Plugin.inserthtml === 'base' ){
				dom.execCommand('inserthtml', false, html);
				
			}else if( range && range.insertNode){
				if( !range.collapsed ){
					// 删除选中的内容
					range.deleteContents();
				}
				
				// 在chrome浏览器下，这种方式插入html，
				// 才会保证html是原模原样的插入到内容当中
				// 使用inserthtml命令插入，在插入sub pre div 等标签时，可能会被浏览器转换掉
				
				var fragment = range.createContextualFragment( html );
				
				// 2011-09-05 潘雪鹏
				// 解决了插入内容后光标在内容左侧的问题
				// 参考：
				// http://stackoverflow.com/questions/2213961/selection-ranges-in-webkit-safari-chrome
				// http://kjah.iteye.com/blog/422509
				
				var lastChild = fragment.lastChild;
				range.insertNode( fragment );
				//range.setEndAfter( fragment.lastChild ); 直接使用 fragment.lastChild 会出错
				
				// 将光标定位到插入的内容的右侧，默认在左侧
				range.setStartAfter( lastChild );
				range.setEndAfter( lastChild );
				range.collapse(false);//合并范围至末尾
				// 上面对 Gecko 好使，webkit还不行
				
				// 针对 webkit 还需要进行以下处理
				var sel = self.win.getSelection();
				sel.removeAllRanges();
				sel.addRange(range);
			}else{
				//dom.execCommand('inserthtml', false, html);
				$('body', dom).append( html );
			}
		}
	},
	
	getRange: function(){
		this.init();
		var win = this.win, doc = this.dom, selection, range;
		// IE，仅IE支持document.selection
		if (doc.selection) { 
			range = doc.selection.createRange();
			//if(!range){range = doc.body.createTextRange();}
		} else if (win.getSelection) {
			// W3C
			// 但是IE9支持win.getSelection，所以还是需要先判断doc.selection(only ie)
			selection = win.getSelection();
			if (selection.getRangeAt) {
				try{
					range = selection.getRangeAt(0);
				}catch(e){}
			} else { // for Old Webkit! 高版本的已经支持 getRangeAt
				range = doc.createRange();
				range.setStart(selection.anchorNode, selection.anchorOffset);
				range.setEnd(selection.focusNode, selection.focusOffset);
			}
			//if(!range){range = doc.createRange();}
		}
		return range;
	},
	
	// 在编辑区域mouseup\keyup时会触发
	saveRange: function() {
		var self = this, range;
		self.init();
		
		// 2011-07-21 潘雪鹏
		// 此处执行 focus() 会导致点击外层有div的图片时，将光标焦点定位到最上面
		//Consts.IE && self.win.focus();
		
		range = self.getRange();
		self.ranges[E.eid] = range;
		return range;
	},
	
	//获取光标处的元素
	getElementByCursor: function( ){
		var self = this;
		self.init();
		var dom = self.dom,
			win = self.win,
			range = self.getRange(),
			elem = dom.body;
			
		if( Consts.IE ){
			
			// var sel = self.getSelection(win, dom);
			// if(sel.type){
				// switch (sel.type) {
					// case "Text":
					// case "None":
						// return range.parentElement();
					// case "Control":
						// return range.item(0);
					// default:
						// return dom.body;
				// }
			// }
			
			elem = range.item ? range.item(0) : range.parentElement();
		}else {
			try{
				var elem = range.commonAncestorContainer;
				if (!range.collapsed 
					&& range.startContainer == range.endContainer 
					&& range.startOffset - range.endOffset < 2
					&& range.startContainer.hasChildNodes()
				){
					elem = range.startContainer.childNodes[range.startOffset];
				}
				while (elem.nodeType == 3 && elem.parentNode) {
					elem = elem.parentNode;
				}
			}catch(e){
				// log
				
			}
		}
		
		return elem;
	},
	
	getCommonAncestor: function(range) {
		return range.startContainer || // w3c
			   (range.parentElement && range.parentElement()) || // ms TextRange
			   (range.commonParentElement && range.commonParentElement()); // ms IHTMLControlRange
	},
	
	// 将光标移到元素后面
	cursorToNext:function( el ){
		var self = this,
			win = self.win,
			dom = self.dom;

		if ( Consts.IE ){
			try{
				//el.insertAdjacentHTML("afterEnd",'<a name="temp_ref">&nbsp;</a>');
				$(el).after('<span> </span>');
				range = dom.body.createTextRange();
				range.moveToElementText(el);
				range.collapse(false);
				range.move('character',1);
				range.select();
			}catch(e){}
		}else{
			$(el).after('<span> </span>');
			var span = $(el).next();
			range = Selection.saveRange();
			range.setStartAfter(span[0]);
			range.collapse(true);
			
			// webkit
			var sel = Selection.get();
			sel.removeAllRanges();
			sel.addRange(range);
		}
		
		Selection.saveRange();
		var elem = Selection.getElementByCursor();
		if( elem ){
			Event.setElement( $(elem) );
		}
	},
	
	//选中给定文本区域
	selectedText: function( text ) {
		var self = this, bool=false;
		if(text && $.trim(text).length){
			var range = self.getRange();
			if( Consts.IE ){
				if(range && range.findText(text)){
					range.select();
					bool= true;
				}
			}else{
				var win = E.get('window');
				bool= win.find(text);
			}
			self.saveRange();
		}
		return bool;
	},
	
	/*
		设置光标位置
		@object node : 需要设置位置节点，为空则重置到区域开始位置
		@boolean dir : 光标设置在node节点前面或者后面[true:前面，false:后面]
	*/
	selectNode: function(node, dir) {
		var self = this,
			dom = E.get('document');
			
		node = (typeof node === 'object') ? node : dom.body.childNodes[0];
		
		E.focus();
		var range = self.saveRange();
		if(Consts.IE) {
			range.moveToElementText(node);
			range.select();
			if( typeof dir !== 'undefined' ){
				range.collapse(dir);
				// 2012-02-08 潘雪鹏
				// range.collapse 后面还需要再次执行select，否则可能不会折叠光标
				range.select();
			}
		} else {
			range.selectNodeContents(node);
			if( typeof dir !== 'undefined' ){
				range.collapse(dir);
			}
			
			var sel = Selection.get();
			sel.removeAllRanges();
			sel.addRange(range);
		}
	},
	
	getBookmark: function( ){
		this.init();
		var win = this.win, dom = this.dom, selection, range;
		try{
			// IE，仅IE支持document.selection
			if ( Consts.IE ) {
				range = dom.selection.createRange();
				return range.getBookmark();
			}/* else if (win.getSelection) {
			
				var bookmark = '<a name="bookmark">.</a>';
				E.pasteHTML(bookmark);
			
			}*/
		}catch(e){}
	},
	
	clearBookmark: function( ){
		/*
		var bookmark = E.$("a[name=bookmark]");
		if( bookmark.size() ){
			bookmark[0].parentNode.removeChild(bookmark[0]);
		}
		*/
	},
	
	moveToBookmark: function( bookmark ) {
		this.init();
		var self = this, win = this.win, doc = this.dom, selection, range;
		// IE，仅IE支持document.selection
		if ( Consts.IE ) {
			if (bookmark && doc.body.createTextRange) { // Internet Explorer
				range = doc.body.createTextRange();
				range.moveToBookmark( bookmark );
				range.select();
			}
		}/* else {
		
			bookmark = E.$("a[name=bookmark]");
			if( bookmark.size() ){
				range = self.getRange();
				if( range ){
					range.setStartAfter(bookmark[0]);
					range.setEndAfter(bookmark[0]);
					bookmark[0].parentNode.removeChild(bookmark[0]);
					selection = self.get();
					selection.removeAllRanges();
					selection.addRange(range);
				}
			}
		
		}*/
	}
}


})(jQEditor, jQuery);